/**
 *******************************************************************************
 * @FileName  : track.c
 * @Author    : GaoQiu
 * @CreateDate: 2020-02-18
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#include <stdio.h>
#include <stdarg.h>
#include "track.h"

#include "utils/fifo.h"
#include "utils/heap.h"

#include "component/spp/spp_api.h"

#define SPP_EN   1

/**
 *******************************************************************************
 * Print Track Start
 *******************************************************************************
 */
print_func_t _print = NULL;//vprintf;

#define TRACK_BUF_SIZE  256
uint8_t trackBuf[TRACK_BUF_SIZE] = {0};

#define DEBUG_BUF_SIZE  128
#define DEBUG_BUF_NUM   16

FIFO_DEFINE(debugFifo, DEBUG_BUF_SIZE, DEBUG_BUF_NUM);

void TrackPushData(DebugFrame_t pBuf)
{
	if(!FIFO_IsFull(&debugFifo))
	{
		DebugFrame_t *p = (DebugFrame_t *)FIFO_GetWriteBuf(&debugFifo);
		*p = pBuf;
		FIFO_MoveToNextWriteBuf(&debugFifo);
	}
}

void TrackStart(void)
{
#if 0
	uint8_t w = debugFifo.w;

	while(w != debugFifo.r)
	{
		DebugFrame_t *pBuf = (DebugFrame_t *)FIFO_GetReadBuf(&debugFifo);

		switch(pBuf->type)
		{
		case TRACK_DATA_TYPE_SCH:
		case TRACK_DATA_TYPE_SLV:
		{
			Timing_t *pTiming = &pBuf->timing;

			if(pBuf->type == TRACK_DATA_TYPE_SCH)
			{
			#if 1
				TRACK_INFO("SCH -> ");
				TRACK("state:%u, handle:%u, prio:%u, ap1:%u, ep1:%u, w1:%u, select:%u\r\n",
						pTiming->state,
						pTiming->connHandle,
						pTiming->prio,
						pTiming->anchorPoint1,
						pTiming->endAnchorPoint1,
						pTiming->windowWiden1,
						pTiming->selectedConnHandle);
			#else
				TRACK_INFO("SCH -> ");
				TRACK("state:%u, ap1:%u, ep1:%u, err1:%04d, iner:%04u,  ap2:%u, ep2:%u, err2:%d,  w1:%u, w2:%u\r\n",
						pTiming->state,
						pTiming->anchorPoint1,
						pTiming->endAnchorPoint1,
						pTiming->err1,
						pTiming->endAnchorPoint1 - pTiming->anchorPoint1,
						pTiming->anchorPoint2,
						pTiming->endAnchorPoint2,
						pTiming->endAnchorPoint2 - pTiming->anchorPoint2,
						pTiming->windowWiden1,
						pTiming->windowWiden2);
			#endif
			}
			else if(pBuf->type == TRACK_DATA_TYPE_SLV){
				TRACK_INFO("SLV -> ");
				TRACK("state:%u, ap1:%u, ep1:%u, err1:%d\r\n",
						pTiming->state,
						pTiming->anchorPoint1,
						pTiming->endAnchorPoint1,
						pTiming->endAnchorPoint1 - pTiming->anchorPoint1);
			}
			break;
		}
		case TRACK_DATA_TYPE_ENC:
			break;

		case TRACK_DATA_TYPE_SCAN:
		{
			ScanFormat_t *pScan = &pBuf->scan;
			TRACK_INFO("SCAN -> ");
			TRACK("id:%u, curTick:%u, endPoint:%u, winTimeUs:%u\r\n",
					pScan->id,
					pScan->curTick,
					pScan->endAnchorPoint,
					pScan->winTimeUs);
			break;
		}
		case TRACK_DATA_TYPE_BB:
		{
			BBFormat_t *pBB = &pBuf->bb;
			TRACK_INFO("BB -> ");
			TRACK("t0:%u, t1:%u, cmp:%u\r\n", pBB->t0, pBB->t1, pBB->cmp);
			break;
		}
		default:
			break;
		}

		FIFO_MoveToNextReadBuf(&debugFifo);
	}
#endif
}


void TrackInit(void)
{
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_GPIO, ENABLE);

#ifdef IP_108
  #if 1
    CLK_APB1PeriphClockCmd(CLK_APB1Periph_UART0, ENABLE);

    // UART
    GPIO_Init(TRACK_UART_TX_PIN, GPIO_Mode_PP_Out);
    GPIO_Init(TRACK_UART_RX_PIN, GPIO_Mode_In);
    GPIO_SetAFunc(TRACK_UART_TX_PIN, TRACK_UART_TX_PIN_AF);
    GPIO_SetAFunc(TRACK_UART_RX_PIN, TRACK_UART_RX_PIN_AF);
  #else
    CLK_APB2PeriphClockCmd(CLK_APB2Periph_UART1, ENABLE);

    // UART
//    GPIO_EnableDigitalPath(P0, BIT6);
//    GPIO_EnableDigitalPath(P0, BIT7);
//    GPIO_SetMode(P0, BIT(6), GPIO_MODE_OUTPUT);
//    GPIO_SetMode(P0, BIT(7), GPIO_MODE_OUTPUT);
//    SYS_SET_MFP(P0, 6, UART1_TX);
//    SYS_SET_MFP(P0, 7, UART1_RX);

    GPIO_Init(TRACK_UART_TX_PIN, GPIO_Mode_PP_Out);
    GPIO_Init(TRACK_UART_RX_PIN, GPIO_Mode_In);
    GPIO_SetAFunc(TRACK_UART_TX_PIN, TRACK_UART_TX_PIN_AF);
    GPIO_SetAFunc(TRACK_UART_RX_PIN, TRACK_UART_RX_PIN_AF);
  #endif

#elif defined(IP_107)
    CLK_APB1PeriphClockCmd(CLK_APB1Periph_UART0, ENABLE);

    // UART
    GPIO_Init(TRACK_UART_TX_PIN, GPIO_Mode_PP_Out);
    GPIO_Init(TRACK_UART_RX_PIN, GPIO_Mode_In);
    GPIO_SetAFunc(TRACK_UART_TX_PIN, TRACK_UART_TX_PIN_AF);
    GPIO_SetAFunc(TRACK_UART_RX_PIN, TRACK_UART_RX_PIN_AF);
#endif

    UART_InitTypeDef uartInit;
    uartInit.UART_BaudRate = TRACK_BAUDRATE;
    uartInit.UART_LineCtrl = Uart_Line_8n1;
    UART_Init(TRACK_UART_PORT, &uartInit);

    UART_EnableFifo(TRACK_UART_PORT);//ENABLE FIFO mode

    UART_ResetRxFifo(TRACK_UART_PORT);

    UART_SetRxTrigger(TRACK_UART_PORT, UART_RX_FIFO_HALF_FULL);
    UART_EnableIrq(TRACK_UART_PORT, UART_IRQ_RECV_DATA_AVL);

    NVIC_EnableIRQ(TRACK_UART_IRQn);

#if SPP_EN
    /* SPP Init. */
    SPP_Init(debugFifoBuf, debugFifo.size * FIFO_GetBufTotalNum(&debugFifo));
#endif
}

#if SPP_EN
void TRACK_UART_IRQHandler(void)
{
	uint32_t res = UART_GetLineStatus(TRACK_UART_PORT);

	if(res & UART_LINE_DATA_RDY)
	{
		uint32_t rxNum = UART_GetRxFifoLevel(TRACK_UART_PORT);

		for(int i=0; i<rxNum; i++){
			uint8_t data = UART_ReceiveData(TRACK_UART_PORT);
			SPP_RxDataHandler(data);
		}
	}
}
#endif

void TrackRegisterPrint(print_func_t func)
{
	_print = func;
}

uint32_t TrackHexToString(char *pStr, uint32_t bufSize, uint8_t *pData, uint32_t len)
{
	uint32_t cnt = 0;
	uint8_t ascii[] = "0123456789ABCDEF";

	for(int i=0; i<len; i++)
	{
		if(cnt + 3 >= bufSize){
			break;
		}

		uint8_t h = (pData[i]>>4) & 0x0f;
		uint8_t l = pData[i] & 0x0f;

		pStr[cnt++] = ascii[h];
		pStr[cnt++] = ascii[l];
		pStr[cnt++] = ' ';
	}

	return cnt;
}

int TrackPrint(char *fmt, ...)
{
#if 0
	int cnt = 0;
	if(_print)
	{
		va_list ap;
		va_start(ap, fmt);
		cnt = _print(fmt, ap);
		va_end(ap);
	}
	return cnt;
#else
	va_list ap;
	//char buf[256] = {0};
	char *buf = trackBuf;

	va_start(ap, fmt);
	int cnt = vsnprintf(buf, sizeof(trackBuf)-1, fmt, ap);
	buf[cnt] = 0;
	va_end(ap);

	for(int i=0; i<cnt; i++)
	{
		while(!UART_IsTxFifoEmpty(TRACK_UART_PORT)){} // for FIFO mode
		//while(!(TRACK_UART_PORT->LSR & BIT(6))){} // for normal mode
		UART_SendData(TRACK_UART_PORT, buf[i]);
		while(!UART_IsTxFifoEmpty(TRACK_UART_PORT)){} // for FIFO mode
	}
	return cnt;
#endif
}

int __io_putchar(char ch)
{
	while(!UART_IsTxFifoEmpty(TRACK_UART_PORT)){} // for FIFO mode
	//while(!(TRACK_UART_PORT->LSR & BIT(6))){} // for normal mode
	UART_SendData(TRACK_UART_PORT, ch);
	while(!UART_IsTxFifoEmpty(TRACK_UART_PORT)){} // for FIFO mode

	return ch;
}

void TrackData(uint8_t *pdata, uint32_t len)
{
	int cnt = 0;
	uint8_t *p = pdata;

	while(len > 0)
	{
		uint32_t fragLen = len > 16 ? 16:len;
		char buf[256] = {0};

		cnt = TrackHexToString(buf, sizeof(buf)-1, p, fragLen);
		buf[cnt] = 0;
		TrackPrint("\t%s\r\n", buf);

		len -= fragLen;
		p += fragLen;
	}
}

void TrackPrintSystemInfo(void)
{
	extern uint32_t BB_MemAvailableSize(void);

    TRACK_INFO("System Start...\r\n");
    TRACK_INFO("System Clock: %d Hz\r\n", SystemCoreClock);
    TRACK_INFO("Heap Free Size: %u B\r\n", HeapGetAvailableSize());
    TRACK_INFO("BB Free Size: %u B\r\n", BB_MemAvailableSize());
}

/**
 *******************************************************************************
 * ASSERT
 *******************************************************************************
 */
/**
 * 	char fn[30],*p;
 *  strcpy(fn,(p=strrchr(pcFile,'/')) ? p+1 : pcFile);
 */
void assert_failed(uint8_t exp, uint32_t errCode, char *fileName, int line)
{
	if(exp == 0){
		TRACK_ERR("ASSERT: File: %s Line: %d Error:0x%08x\r\n", fileName, line, errCode);
		while(1) { TRACK_START(); }
	}
}

/**
 *******************************************************************************
 * Timing Track Start
 *******************************************************************************
 */
void TrackTimingInit(void)
{
#if TIMING_TRACK_EN
	CLK_AHBPeriphClockCmd(CLK_AHBPeriph_GPIO, ENABLE);

	uint16_t gpioPin[] = {
		  DBG_CHN0_PIN,  DBG_CHN1_PIN,  DBG_CHN2_PIN,  DBG_CHN3_PIN,
		  DBG_CHN4_PIN,  DBG_CHN5_PIN,  DBG_CHN6_PIN,  DBG_CHN7_PIN,
		  DBG_CHN8_PIN,  DBG_CHN9_PIN,  DBG_CHN10_PIN, DBG_CHN11_PIN,
		  DBG_CHN12_PIN, DBG_CHN13_PIN, DBG_CHN14_PIN, DBG_CHN15_PIN,
	};

	for(int i=0; i<COUNTOF(gpioPin); i++)
	{
		GPIO_Init(gpioPin[i],     GPIO_Mode_PP_Out);
		GPIO_SetAFunc(gpioPin[i], GPIO_AF_GPIO);
		GPIO_ResetBit(gpioPin[i]);
	}
#endif
}
